package com.gobang.common.filter;

import com.gobang.common.exception.BusinessException;
import com.gobang.common.service.TokenAuthService;
import com.gobang.common.service.TokenAuthService.AuthUser;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;

/**
 * JWT认证过滤器
 * 拦截请求，验证JWT Token并设置认证信息
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private final TokenAuthService tokenAuthService;

    /**
     * Token请求头名称
     */
    private static final String AUTHORIZATION_HEADER = "Authorization";

    /**
     * Token前缀
     */
    private static final String TOKEN_PREFIX = "Bearer ";

    /**
     * 需要跳过JWT过滤的路径（完全不做Token解析）
     */
    private static final String[] EXCLUDE_PATHS = {
            "/api/test/**"
    };

    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            // 从请求头中提取Token
            String jwt = extractJwtFromRequest(request);

            if (StringUtils.hasText(jwt)) {
                try {
                    // 使用统一的 TokenAuthService 进行 JWT + Redis 双重校验，并获取认证用户信息
                    AuthUser authUser = tokenAuthService.validateToken(jwt);
                    setAuthentication(authUser, request);
                } catch (BusinessException e) {
                    // 认证失败，记录业务异常日志但不打断过滤器链，交由后续安全配置处理未认证请求
                    log.debug("Token 认证失败: code={}, message={}", e.getCode(), e.getMessage());
                }
            }
        } catch (Exception e) {
            log.error("无法设置用户认证信息: {}", e.getMessage());
        }

        // 继续过滤器链
        filterChain.doFilter(request, response);
    }

    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) {
        String path = request.getServletPath();
        return Arrays.stream(EXCLUDE_PATHS)
                .anyMatch(pattern -> PATH_MATCHER.match(pattern, path));
    }

    /**
     * 从请求头中提取JWT Token
     *
     * @param request HTTP请求
     * @return JWT Token（不含Bearer前缀）
     */
    private String extractJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader(AUTHORIZATION_HEADER);

        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
            return bearerToken.substring(TOKEN_PREFIX.length());
        }

        return null;
    }

    /**
     * 设置Spring Security认证信息
     *
     * @param authUser 认证用户信息
     * @param request  HTTP请求
     */
    private void setAuthentication(AuthUser authUser, HttpServletRequest request) {
        // 创建认证对象
        UsernamePasswordAuthenticationToken authentication =
                new UsernamePasswordAuthenticationToken(
                        authUser.getUsername(),
                        null,
                        Collections.singletonList(new SimpleGrantedAuthority(authUser.getRole()))
                );

        // 设置详细信息
        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

        // 将认证信息存入Security上下文
        SecurityContextHolder.getContext().setAuthentication(authentication);

        log.debug("设置用户认证成功: userId={}, username={}, role={}", authUser.getUserId(), authUser.getUsername(), authUser.getRole());
    }

}

